package com.summer.bilibili.util;

import cn.hutool.core.net.Ipv4Util;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import org.json.JSONObject;

import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.stream.Stream;

/**
 * IP工具类：提供IP地址相关的实用功能
 * 包含IP定位、主机信息获取、客户端真实IP提取等功能
 * <p>
 * 优化要点：
 * 1. 使用常量管理魔法字符串
 * 2. 增强网络请求的健壮性（超时控制/资源自动释放）
 * 3. 统一异常处理机制
 * 4. 消除代码重复
 */
public class IpUtils {

    // 常量定义区域 -----------------------------------------------
    /**
     * 未知标识常量
     */
    private static final String UNKNOWN = "未知";
    /**
     * 本地标识常量
     */
    private static final String LOCAL = "本地";
    /**
     * IPv4本地地址
     */
    private static final String LOCAL_IPV4 = "127.0.0.1";
    /**
     * IPv6本地地址
     */
    private static final String LOCAL_IPV6 = "0:0:0:0:0:0:0:1";
    /**
     * IP查询API模板
     */
    private static final String IP_QUERY_URL = "https://whois.pconline.com.cn/ipJson.jsp?ip=%s&json=true";
    /**
     * HTTP请求超时时间(毫秒)
     */
    private static final int HTTP_TIMEOUT = 5000;

    /**
     * 获取IP对应的真实地理位置
     *
     * @param ip 需要查询的IP地址
     * @return 格式为"省份+城市"的字符串，如"广东省深圳市"
     * <p>
     * 实现逻辑：
     * 1. 空值检查 → 返回空字符串
     * 2. 内网IP检查 → 返回"本地"
     * 3. 调用第三方API获取地理信息
     * 4. 解析JSON返回省市级联结果
     */
    public static String getRealLocation ( String ip ) {
        // 空值安全处理
        if ( StrUtil.isBlank ( ip ) ) return StrUtil.EMPTY;

        // 内网IP直接返回本地标识
        if ( Ipv4Util.isInnerIP ( ip ) ) return LOCAL;

        try (HttpResponse response = HttpUtil.createGet ( String.format ( IP_QUERY_URL , ip ) )
                .timeout ( HTTP_TIMEOUT )  // 关键优化：添加超时控制
                .execute ( )) {

            // 仅处理200响应
            if ( response.isOk ( ) ) {
                JSONObject result = new JSONObject ( response.body ( ) );
                // 安全获取省市信息（空值转空字符串）
                return StrUtil.nullToEmpty ( result.optString ( "pro" ) ) +
                        StrUtil.nullToEmpty ( result.optString ( "city" ) );
            }
        } catch (Exception e) {
            // 实际项目应替换为日志框架记录
            System.err.println ( "IP查询服务异常: " + e.getMessage ( ) );
        }
        return StrUtil.EMPTY;  // 统一返回空字符串
    }

    /**
     * 网络信息类型枚举
     * 明确指定获取主机名或IP地址
     */
    private enum NetworkInfoType {
        /**
         * 主机名类型
         */
        HOST_NAME,
        /**
         * IP地址类型
         */
        HOST_ADDRESS
    }

    /**
     * 统一获取网络信息方法
     *
     * @param type 信息类型（主机名/IP地址）
     * @return 请求的网络信息
     * <p>
     * 设计说明：
     * 1. 封装InetAddress的重复调用逻辑
     * 2. 集中处理UnknownHostException
     */
    private static String getNetworkInfo ( NetworkInfoType type ) {
        try {
            InetAddress address = InetAddress.getLocalHost ( );
            return type == NetworkInfoType.HOST_NAME ?
                    address.getHostName ( ) : address.getHostAddress ( );
        } catch (UnknownHostException e) {
            System.err.println ( "网络信息获取失败: " + e.getMessage ( ) );
            return UNKNOWN;  // 统一未知标识
        }
    }

    /**
     * 获取当前主机名
     *
     * @return 主机名字符串（失败返回"未知"）
     */
    public static String getHostName () {
        return getNetworkInfo ( NetworkInfoType.HOST_NAME );
    }

    /**
     * 获取当前主机IP地址
     *
     * @return IP地址字符串（失败返回"未知"）
     */
    public static String getHostAddress () {
        return getNetworkInfo ( NetworkInfoType.HOST_ADDRESS );
    }

    /**
     * 获取客户端真实IP（支持代理场景）
     *
     * @param request HTTP请求对象
     * @return 客户端真实IP（自动转换IPv6本地地址）
     * <p>
     * 核心流程：
     * 1. 按优先级检查代理头信息：x-real-ip → x-forwarded-for
     * 2. 前两者不存在时使用request.getRemoteAddr()
     * 3. 转换IPv6本地地址为IPv4格式
     * <p>
     * 注意事项：
     * 需在Nginx配置中添加：
     * proxy_set_header X-Real-IP $remote_addr;
     * proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
     */
    public static String getIpaddr ( HttpServletRequest request ) {
        // 使用Stream API优化头部优先级检查
        String ip = Stream.of ( "x-real-ip" , "x-forwarded-for" )
                .map ( request::getHeader )         // 获取头部值
                .filter ( StrUtil::isNotBlank )     // 过滤空值
                .findFirst ( )                     // 获取首个有效值
                .orElseGet ( request::getRemoteAddr ); // 降级方案

        // 统一本地IP格式
        return LOCAL_IPV6.equals ( ip ) ? LOCAL_IPV4 : ip;
    }
}
